home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 126-150 / disk_149 / less / src / ch.c next >
C/C++ Source or Header  |  1992-05-06  |  8KB  |  419 lines

  1. /*
  2.  * Low level character input from the input file.
  3.  * We use these special purpose routines which optimize moving
  4.  * both forward and backward from the current read pointer.
  5.  */
  6.  
  7. #include "less.h"
  8.  
  9. public int file = -1;    /* File descriptor of the input file */
  10.  
  11. /*
  12.  * Pool of buffers holding the most recently used blocks of the input file.
  13.  */
  14. #define BUFSIZ    1024
  15. struct buf {
  16.     struct buf *next, *prev;
  17.     long block;
  18.     char data[BUFSIZ];
  19. };
  20. static struct buf *bufs = NULL;
  21. public int nbufs;
  22.  
  23. /*
  24.  * The buffer pool is kept as a doubly-linked circular list,
  25.  * in order from most- to least-recently used.
  26.  * The circular list is anchored by buf_anchor.
  27.  */
  28. static struct {
  29.     struct buf *next, *prev;
  30. } buf_anchor;
  31. #define    END_OF_CHAIN    ((struct buf *)&buf_anchor)
  32. #define    buf_head    buf_anchor.next
  33. #define    buf_tail    buf_anchor.prev
  34.  
  35. /*
  36.  * If we fail to allocate enough memory for buffers, we try to limp
  37.  * along with a minimum number of buffers.  
  38.  */
  39. #define    DEF_NBUFS    2    /* Minimum number of buffers */
  40.  
  41. extern int clean_data;
  42. extern int ispipe;
  43. extern int sigs;
  44.  
  45. #if LOGFILE
  46. extern int logfile;
  47. #endif
  48.  
  49. /*
  50.  * Current position in file.
  51.  * Stored as a block number and an offset into the block.
  52.  */
  53. static long ch_block;
  54. static int ch_offset;
  55.  
  56. /* 
  57.  * Length of file, needed if input is a pipe.
  58.  */
  59. static POSITION ch_fsize;
  60.  
  61. /*
  62.  * Largest block number read if input is standard input (a pipe).
  63.  */
  64. static long last_piped_block;
  65.  
  66. /*
  67.  * Get the character pointed to by the read pointer.
  68.  * ch_get() is a macro which is more efficient to call
  69.  * than fch_get (the function), in the usual case 
  70.  * that the block desired is at the head of the chain.
  71.  */
  72. #define    ch_get()   ((buf_head->block == ch_block) ? \
  73.             buf_head->data[ch_offset] : fch_get())
  74.     static int
  75. fch_get()
  76. {
  77.     register struct buf *bp;
  78.     register int n;
  79.     register int end;
  80.     POSITION pos;
  81.  
  82.     /*
  83.      * Look for a buffer holding the desired block.
  84.      */
  85.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  86.         if (bp->block == ch_block)
  87.             goto found;
  88.     /*
  89.      * Block is not in a buffer.  
  90.      * Take the least recently used buffer 
  91.      * and read the desired block into it.
  92.      */
  93.     bp = buf_tail;
  94.     bp->block = ch_block;
  95.     pos = ch_block * BUFSIZ;
  96.     if (ispipe)
  97.     {
  98.         /*
  99.          * The block requested should be one more than
  100.          * the last block read.
  101.          */
  102.         if (ch_block != ++last_piped_block)
  103.         {
  104.             /* This "should not happen". */
  105.             char message[80];
  106.             sprintf(message, "Pipe error: last %ld, want %ld\n",
  107.                 (long)last_piped_block-1, (long)ch_block);
  108.             error(message);
  109.             quit();
  110.         }
  111.     } else
  112.         lseek(file, pos, 0);
  113.  
  114.     /*
  115.      * Read the block.  This may take several reads if the input
  116.      * is coming from standard input, due to the nature of pipes.
  117.      */
  118.     end = 0;
  119.     while ((n = read(file, &bp->data[end], BUFSIZ-end)) > 0)
  120.         if ((end += n) >= BUFSIZ)
  121.             break;
  122.  
  123.     if (n < 0)
  124.     {
  125.         error("read error");
  126.         quit();
  127.     }
  128.  
  129. #if LOGFILE
  130.     /*
  131.      * If we have a log file, write this block to it.
  132.      */
  133.     if (logfile >= 0 && end > 0)
  134.         write(logfile, bp->data, end);
  135. #endif
  136.  
  137.     /*
  138.      * Set an EOF marker in the buffered data itself.
  139.      * Then ensure the data is "clean": there are no 
  140.      * extra EOF chars in the data and that the "meta"
  141.      * bit (the 0200 bit) is reset in each char.
  142.      */
  143.     if (end < BUFSIZ)
  144.     {
  145.         ch_fsize = pos + end;
  146.         bp->data[end] = EOF;
  147.     }
  148.  
  149.     if (!clean_data)
  150.         while (--end >= 0)
  151.         {
  152.             bp->data[end] &= 0177;
  153.             if (bp->data[end] == EOF)
  154.                 bp->data[end] = '@';
  155.         }
  156.  
  157.     found:
  158.     /* if (buf_head != bp) {this is guaranteed by the ch_get macro} */
  159.     {
  160.         /*
  161.          * Move the buffer to the head of the buffer chain.
  162.          * This orders the buffer chain, most- to least-recently used.
  163.          */
  164.         bp->next->prev = bp->prev;
  165.         bp->prev->next = bp->next;
  166.  
  167.         bp->next = buf_head;
  168.         bp->prev = END_OF_CHAIN;
  169.         buf_head->prev = bp;
  170.         buf_head = bp;
  171.     }
  172.     return (bp->data[ch_offset]);
  173. }
  174.  
  175. #if LOGFILE
  176. /*
  177.  * Close the logfile.
  178.  * If we haven't read all of standard input into it, do that now.
  179.  */
  180.     public void
  181. end_logfile()
  182. {
  183.     static int tried;
  184.  
  185.     if (logfile < 0)
  186.         return;
  187.     if (!tried && ch_fsize == NULL_POSITION)
  188.     {
  189.         tried = 1;
  190.         lower_left();
  191.         clear_eol();
  192.         so_enter();
  193.         putstr("finishing logfile... (interrupt to abort)");
  194.         so_exit();
  195.         flush();
  196.         while (sigs == 0 && ch_forw_get() != EOF)
  197.             ;
  198.     }
  199.     close(logfile);
  200.     logfile = -1;
  201. }
  202. #endif
  203.  
  204. /*
  205.  * Determine if a specific block is currently in one of the buffers.
  206.  */
  207.     static int
  208. buffered(block)
  209.     long block;
  210. {
  211.     register struct buf *bp;
  212.  
  213.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  214.         if (bp->block == block)
  215.             return (1);
  216.     return (0);
  217. }
  218.  
  219. /*
  220.  * Seek to a specified position in the file.
  221.  * Return 0 if successful, non-zero if can't seek there.
  222.  */
  223.     public int
  224. ch_seek(pos)
  225.     register POSITION pos;
  226. {
  227.     long new_block;
  228.  
  229.     new_block = pos / BUFSIZ;
  230.     if (!ispipe || new_block == last_piped_block + 1 || buffered(new_block))
  231.     {
  232.         /*
  233.          * Set read pointer.
  234.          */
  235.         ch_block = new_block;
  236.         ch_offset = pos % BUFSIZ;
  237.         return (0);
  238.     }
  239.     return (1);
  240. }
  241.  
  242. /*
  243.  * Seek to the end of the file.
  244.  */
  245.     public int
  246. ch_end_seek()
  247. {
  248.     if (ispipe)
  249.     {
  250.         /*
  251.          * Do it the slow way: read till end of data.
  252.          */
  253.         while (ch_forw_get() != EOF)
  254.             ;
  255.     } else
  256.     {
  257.         (void) ch_seek((POSITION)(lseek(file, (offset_t)0, 2)));
  258.     }
  259.     return (0);
  260. }
  261.  
  262. /*
  263.  * Seek to the beginning of the file, or as close to it as we can get.
  264.  * We may not be able to seek there if input is a pipe and the
  265.  * beginning of the pipe is no longer buffered.
  266.  */
  267.     public int
  268. ch_beg_seek()
  269. {
  270.     register struct buf *bp, *firstbp;
  271.  
  272.     /*
  273.      * Try a plain ch_seek first.
  274.      */
  275.     if (ch_seek((POSITION)0) == 0)
  276.         return (0);
  277.  
  278.     /*
  279.      * Can't get to position 0.
  280.      * Look thru the buffers for the one closest to position 0.
  281.      */
  282.     firstbp = bp = buf_head;
  283.     if (bp == END_OF_CHAIN)
  284.         return (1);
  285.     while ((bp = bp->next) != END_OF_CHAIN)
  286.         if (bp->block < firstbp->block)
  287.             firstbp = bp;
  288.     ch_block = firstbp->block;
  289.     ch_offset = 0;
  290.     return (0);
  291. }
  292.  
  293. /*
  294.  * Return the length of the file, if known.
  295.  */
  296.     public POSITION
  297. ch_length()
  298. {
  299.     if (ispipe)
  300.         return (ch_fsize);
  301.     return ((POSITION)(lseek(file, (offset_t)0, 2)));
  302. }
  303.  
  304. /*
  305.  * Return the current position in the file.
  306.  */
  307.     public POSITION
  308. ch_tell()
  309. {
  310.     return (ch_block * BUFSIZ + ch_offset);
  311. }
  312.  
  313. /*
  314.  * Get the current char and post-increment the read pointer.
  315.  */
  316.     public int
  317. ch_forw_get()
  318. {
  319.     register int c;
  320.  
  321.     c = ch_get();
  322.     if (c != EOF && ++ch_offset >= BUFSIZ)
  323.     {
  324.         ch_offset = 0;
  325.         ch_block ++;
  326.     }
  327.     return (c);
  328. }
  329.  
  330. /*
  331.  * Pre-decrement the read pointer and get the new current char.
  332.  */
  333.     public int
  334. ch_back_get()
  335. {
  336.     register int c;
  337.  
  338.     if (--ch_offset < 0)
  339.     {
  340.         if (ch_block <= 0 || (ispipe && !buffered(ch_block-1)))
  341.         {
  342.             ch_offset = 0;
  343.             return (EOF);
  344.         }
  345.         ch_offset = BUFSIZ - 1;
  346.         ch_block--;
  347.     }
  348.     c = ch_get();
  349.     return (c);
  350. }
  351.  
  352. /*
  353.  * Initialize the buffer pool to all empty.
  354.  * Caller suggests that we use want_nbufs buffers.
  355.  */
  356.     public void
  357. ch_init(want_nbufs)
  358.     int want_nbufs;
  359. {
  360.     register struct buf *bp;
  361.     char *calloc();
  362.  
  363.     if (nbufs < want_nbufs)
  364.     {
  365.         /*
  366.          * We don't have enough buffers.  
  367.          * Free what we have (if any) and allocate some new ones.
  368.          */
  369.         if (bufs != NULL)
  370.             free((char *)bufs);
  371.         bufs = (struct buf *) calloc(want_nbufs, sizeof(struct buf));
  372.         nbufs = want_nbufs;
  373.         if (bufs == NULL)
  374.         {
  375.             /*
  376.              * Couldn't get that many.
  377.              * Try for a small default number of buffers.
  378.              */
  379.             char message[80];
  380.             sprintf(message,
  381.               "Cannot allocate %d buffers.  Using %d buffers.", 
  382.               nbufs, DEF_NBUFS);
  383.             error(message);
  384.             bufs = (struct buf *) calloc(DEF_NBUFS, sizeof(struct buf));
  385.             nbufs = DEF_NBUFS;
  386.             if (bufs == NULL)
  387.             {
  388.                 /*
  389.                  * Couldn't even get the smaller number of bufs.
  390.                  * Something is wrong here, don't continue.
  391.                  */
  392.                 sprintf(message, 
  393.                 "Cannot even allocate %d buffers!  Quitting.",
  394.                   DEF_NBUFS);
  395.                 error(message);
  396.                 quit();
  397.                 /*NOTREACHED*/
  398.             }
  399.         }
  400.     }
  401.  
  402.     /*
  403.      * Initialize the buffers to empty.
  404.      * Set up the circular list.
  405.      */
  406.     for (bp = &bufs[0];  bp < &bufs[nbufs];  bp++)
  407.     {
  408.         bp->next = bp + 1;
  409.         bp->prev = bp - 1;
  410.         bp->block = (long)(-1);
  411.     }
  412.     bufs[0].prev = bufs[nbufs-1].next = END_OF_CHAIN;
  413.     buf_head = &bufs[0];
  414.     buf_tail = &bufs[nbufs-1];
  415.     last_piped_block = -1;
  416.     ch_fsize = NULL_POSITION;
  417.     (void) ch_seek((POSITION)0);
  418. }
  419.